﻿namespace TetrisProject
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using TetrisProject.GameObjects;
    using TetrisProject.Interfaces;

    public class Engine
    {
        private IList<GameObject> activeObjects;
        private IBlockFactory factory;
        private ConsoleRenderer renderer;
        private Keyboard keyboard;
        private GameObject fallingBlock;

        private int height;
        private int width;

        public Engine(int height, int width, Keyboard keyboard)
        {
            this.height = height;
            this.width = width;
            this.keyboard = keyboard;

            this.renderer = new ConsoleRenderer(this.height, this.width);
            this.activeObjects = new List<GameObject>();
            this.factory = new BlockFactory(this.width);
        }

        public void Run()
        {
            this.fallingBlock = this.factory.CreateBlock();
            this.activeObjects.Add(this.fallingBlock);

            Timer timer = new Timer(this.TimerCallBack, null, 0, 500);

            while (true)
            {
                this.renderer.ClearBuffer();
                this.keyboard.ProcessInput();
                this.renderer.InsertBlockCollection(
                    new List<IRenderable>(this.activeObjects));

                this.renderer.RenderAll();

                if (this.IsFallingBlockOutOfField())
                {
                    this.fallingBlock = this.factory.CreateBlock();
                    this.activeObjects.Clear();
                    this.activeObjects.Add(this.fallingBlock);
                }
            }
        }

        public void OnSpacePressed()
        {
            this.fallingBlock.Rotate();
            this.UpdateScreen();
        }

        public void OnArrowPressed(ConsoleKey arrow)
        {
            switch (arrow)
            {
                case ConsoleKey.LeftArrow:
                    this.fallingBlock.MoveLeft();
                    break;
                case ConsoleKey.RightArrow:
                    this.fallingBlock.MoveRight();
                    break;
                case ConsoleKey.DownArrow:
                    this.fallingBlock.MoveDown();
                    break;
                default:
                    return;
            }

            this.UpdateScreen();
        }

        private void AddBlock()
        {
            var block = this.factory.CreateBlock();
            this.activeObjects.Add(block);
        }

        private void TimerCallBack(Object o)
        {
            this.fallingBlock.MoveDown();
            this.UpdateScreen();
        }

        private void UpdateScreen()
        {
            this.renderer.ClearBuffer();
            Console.Clear();
            this.renderer.RenderAll();
        }

        private bool IsFallingBlockOutOfField()
        {
            return this.fallingBlock.GetTopLeft().X > this.height + 3;
        }
    }
}
